package com.rankeiot.platform.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RuntimeUtil;
import com.rankeiot.core.anno.Auth;
import com.rankeiot.core.data.response.Resp;
import com.rankeiot.core.util.StringUtil;
import com.rankeiot.platform.PlatformApplication;
import com.rankeiot.platform.PlatformMenu;
import com.sun.management.OperatingSystemMXBean;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.info.BuildProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;

import java.lang.management.ManagementFactory;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.util.*;

@Tag(name = "组织机构")
@Auth(PlatformMenu.CONSOLE)
@RestController
@RequestMapping("console")
@RequiredArgsConstructor
public class ConsoleController {

    final ApplicationContext context;

    final BuildProperties buildProperties;
    final String startTime= DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss");
    private static final String dockerCliPath="/usr/bin/docker";
    public static abstract class AbstractDetector{
        public abstract String getName();
        public abstract Map<String,Object> getInfo();
        public static String formatByteSize(long bytes) {
            if (bytes < 1024) {
                return bytes + " bytes";
            }
            int exp = (int) (Math.log(bytes) / Math.log(1024));
            char pre = "KMGTPE".charAt(exp-1);
            return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
        }
        public static String toGB(long bytes){
            return String.format("%.1f", bytes / Math.pow(1024, 3));
        }
    }
    public static class SystemDetector extends AbstractDetector{

        @Override
        public String getName() {
            return "system";
        }

        @Override
        public Map<String, Object> getInfo() {
            Map<String,Object> info=new HashMap<>();

            info.put("version",System.getProperty("java.version"));
            info.put("vendor",System.getProperty("java.vendor"));
            info.put("system_name",System.getProperty("os.name"));
            info.put("system_version",System.getProperty("os.version"));
            info.put("pid",ProcessHandle.current().pid());
            String dockerId=PlatformApplication.getDockerId();
            if(StringUtil.isNotEmpty(dockerId)){
                info.put("dockerId",dockerId);
                //获取真实pid
               // List<String> lines = RuntimeUtil.execForLines(dockerCliPath, "top",dockerId);
            }
//            info.put("user_dir",System.getProperty("user.dir"));
//            info.put("java_home",System.getProperty("java.home"));
            return info;
        }
    }
    public static class FileDetector extends AbstractDetector{

        private String dir="./";
        public FileDetector(){

        }
        public FileDetector(String dir){
            this.dir=dir;
        }
        public FileDetector(File dir){
            if(dir.isFile()){
                dir=dir.getParentFile();
            }
            this.dir=dir.getAbsolutePath();
        }
        @Override
        public String getName() {
            return "file";
        }

        @Override
        public Map<String, Object> getInfo() {
            Map<String, Object> disk = new LinkedHashMap<>();
            File file=new File(dir);
            long total = file.getTotalSpace();
            long free = file.getFreeSpace();
            long used = total - free;

            float ratio = (total > 0L ? used * 100.0F / total : 0F);

            disk.put("total", formatByteSize(total));
            disk.put("free", formatByteSize(free));
            disk.put("used", formatByteSize(used));
            disk.put("ratio", new BigDecimal(ratio).setScale(2,BigDecimal.ROUND_HALF_UP).toString());
            return disk;
        }
    }
    public static class ProcessDetector extends AbstractDetector {

        @Override
        public String getName() {
            return "process";
        }

        @Override
        public Map<String, Object> getInfo() {


            OperatingSystemMXBean osBean = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();


            // 获取系统总内存
            long totalMemorySize = osBean.getTotalMemorySize();
            long totalFreeMemorySize = osBean.getFreeMemorySize();

            // 获取当前进程的内存占用
            long processMemorySize = Runtime.getRuntime().totalMemory();

            long totalUseMemory=totalMemorySize-totalFreeMemorySize;//-osBean.getTotalSwapSpaceSize();
            // 计算内存使用率
            double memoryUsage = (double) processMemorySize / totalMemorySize * 100;
            double totalUsage=(double) totalUseMemory/totalMemorySize*100;

            double totalCpu=osBean.getCpuLoad();
            if(Double.isInfinite(totalCpu)){
                totalCpu=1;
            }else if(Double.isNaN(totalCpu)){
                totalCpu=0;
            }

            String totalCPU=new BigDecimal(totalCpu*100).setScale(2, RoundingMode.HALF_UP).toString();
            String CPU= new BigDecimal(osBean.getProcessCpuLoad()*100).setScale(2, RoundingMode.HALF_UP).toString();
            String MEM=new BigDecimal(memoryUsage).setScale(2, RoundingMode.HALF_UP).toString();
            String TotalUSE=formatByteSize(totalMemorySize-totalFreeMemorySize-osBean.getTotalSwapSpaceSize());
            if(ProcessHandle.current().pid()==1l){
                File dockerCli = new File(dockerCliPath);
                if(dockerCli.exists()){
                    String dockerId = PlatformApplication.getDockerId();
                    List<String> lines = RuntimeUtil.execForLines(dockerCliPath, "stats","--no-stream",dockerId);
                    if(lines.size()==2){
                        String infoLine=lines.get(1);
                        String[] infos=infoLine.split("\\s+");
                        CPU=infos[2].replace("%","");
                        TotalUSE=infos[3];
                        MEM=infos[6].replace("%","");
                    }
                }
            }
            return Map.of("totalCPU",//            System.out.println("Memory Usage: " + memoryUsage + "%");
                    totalCPU ,
                    "CPU",//            System.out.println("Memory Usage: " + memoryUsage + "%");
                    CPU,
                    "totalMemory",formatByteSize(totalMemorySize),
                    "totalUseMemory",TotalUSE,
                    "processMemory",formatByteSize(processMemorySize),
                    "totalMEM",new BigDecimal(totalUsage).setScale(2,RoundingMode.HALF_UP).toString(),
                    "MEM",MEM
            );
        }
    }
    //jvm
    @GetMapping("jdk_info")
    public Resp getJDKInfo(){
        return Resp.of(new SystemDetector().getInfo());
    }
    //硬盘消息
    @GetMapping("disk_info")
    public Resp getDiskInfo(){
        return Resp.of(new FileDetector().getInfo());
    }
    //进程消息
    @GetMapping("process_info")
    public Resp getProcessInfo(){
        return Resp.of(new ProcessDetector().getInfo());
    }
    @GetMapping("restart")
    public Resp restart() {
        PlatformApplication.restart();
        return Resp.of(context.hashCode());
    }
    @PostMapping("context_id")
    public Resp contextId() {
        return Resp.of(context.hashCode());
    }
    @GetMapping("application_info")
    public Resp applicationInfo() {
        return  Resp.map().set("frameWorkVersion",buildProperties.getVersion())
                .set("lastStartTime",startTime);
    }
    private List<String> getJarFiles(File dir,String path){
        List<String> files=new ArrayList<>();
        if(dir.exists()&&dir.isDirectory()){
            for(File jar:dir.listFiles()){
                String name=jar.getName();
                if(jar.isFile()&&name.endsWith(".jar")){
                    files.add(path+name);
                }

            }
        }
        return files;
    }
    //获取待上传文件
    @GetMapping("update_files")
    public Resp updateFiles(){
        File baseDir=new File("./");
        List<String> files=new ArrayList<>();
        files.addAll(getJarFiles(baseDir,""));
        files.addAll(getJarFiles(new File(baseDir,"lib"),"lib/"));
        return Resp.of(files);
    }
    @PostMapping("update")
    public Resp update(MultipartFile file,String path) throws IOException {
        //获取当前main文件的jar名称

        File dist=new File(new File("./"),path);
        if(!dist.exists()||dist.isDirectory()){
            Resp.fail("路径错误");
        }
        File backDir=new File("_back");
        if(!backDir.exists()){
            backDir.mkdirs();
        }
        //备份文件
        File backFile=new File(backDir,path+"_"+ DateUtil.format(new Date(),"yyyyMMddHHmmss"));
        backFile.getParentFile().mkdirs();
        FileCopyUtils.copy(dist.getAbsoluteFile(),backFile.getAbsoluteFile());

        //保存临时文件
        File temp = File.createTempFile("temp_up",".jar");
        file.transferTo(temp.getAbsoluteFile());
        //更改文件
        try {
           // temp.renameTo(dist);
            FileCopyUtils.copy(temp.getAbsoluteFile(),dist.getAbsoluteFile());
            restart();
        }finally {
            temp.delete();
        }
        return Resp.ok();
    }
}
